package com.caption.core.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.caption.core.annotations.RequireLogin;
import com.caption.core.constants.HeaderConstant;
import com.caption.core.constants.SecurityConstant;
import com.caption.core.local.LocalUser;
import com.caption.core.local.UserRole;
import com.caption.core.local.UserThreadLocal;
import com.caption.core.model.enums.BaseResultEnum;
import com.caption.core.exception.ServiceException;
import com.caption.core.properties.SignProperties;
import com.caption.core.utils.JwtUtils;
import com.caption.core.utils.ResponseUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;

/**
 * @description: 访问接口拦截器
 * @author: 码农
 * @date: 2021-12-24 22:13:39
 */
@Component
@Slf4j
public class AccessInterceptor implements HandlerInterceptor {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
       // log.info("-----------进入拦截器,url={}-----------", requestURI);
        if (handler instanceof ResourceHttpRequestHandler) {
            return true;
        }
        //不是controller 直接放行
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        //链路id
        // String traceId = request.getHeader(HeaderConstant.TRACE_ID_HEADER);
        UserThreadLocal.setRequestUri(requestURI);
        HandlerMethod handlerMethod = (HandlerMethod) handler;

        //获取是否登录的url
        RequireLogin requireLogin = handlerMethod.getMethodAnnotation(RequireLogin.class);
        String token = request.getHeader(HeaderConstant.TOKEN_HEADER);
        //没有登录
        if (requireLogin != null && !StringUtils.isNotBlank(token)) {
            ResponseUtils.out(response, ResponseUtils.resultCode(BaseResultEnum.NOT_LOGIN.getCode(), BaseResultEnum.NOT_LOGIN.getDesc()));
            return false;
        }

        if (StringUtils.isNotBlank(token)) {
            //解析token
            StopWatch stopWatch =new StopWatch();
            stopWatch.start();
            Claims claims = JwtUtils.parseToken(token, SignProperties.jwtSignKey);
            stopWatch.stop();
            log.info("解密耗时={}",stopWatch.getTotalTimeMillis());
            String content = (String) claims.get(SecurityConstant.TOKEN_CONTENT);
            LocalUser localUser = JSONObject.parseObject(content, LocalUser.class);
            UserThreadLocal.setUserInfo(localUser);
            //校验权限
            if (requireLogin != null) {
                List<String> userEnum = Arrays.asList(requireLogin.userEnum());
                if (userEnum.size() != 0) {
                    List<UserRole> roles = localUser.getRoles();
                    if (!CollectionUtils.isEmpty(roles)) {
                        for (UserRole role : roles) {
                            //只要匹配一个角色成功就可以访问
                            if (userEnum.contains(role.getCode())) {
                                return true;
                            }
                        }
                    }
                    throw new ServiceException(BaseResultEnum.NOT_AUTHORITY);
                }
            }
            return true;
        }

        return true;

    }


    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        /**
         * 一定要清空threadLocal,不然会造成内存泄露
         */
        if (handler instanceof HandlerMethod) {
            UserThreadLocal.remove();
        }
    }


}
